// 直升机碰撞箱类：用于处理直升机各部位的碰撞检测
class Hitbox {
    // 构造函数：初始化碰撞箱
    // @param x: 相对于直升机中心的X坐标
    // @param y: 相对于直升机中心的Y坐标
    // @param radius: 碰撞箱半径
    constructor(x, y, radius) {
        this.x = x;
        this.y = y;
        this.radius = radius;
        this.isLanding = false;  // 是否为着陆点
        this.readjusted = false; // 是否已被调整位置（用于碰撞处理）
        this.vital = false;      // 是否为关键部位（受损会导致爆炸）
    }
}

class Chopper extends Entity {

    // 构造函数：初始化直升机
    // @param facing: 朝向（1为右，-1为左）
    constructor(facing = 1) {
        super();

        // 添加到直升机分类桶
        this.buckets.push('chopper');

        // 控制状态
        this.controls = {
            up: false,    // 上升
            left: false,  // 左移
            right: false, // 右移
            shoot: false, // 射击
            down: false,  // 下降
        };

        this.radius = 35;  // 直升机主体半径

        // 武器系统相关
        this.lastShot = 0;        // 上次射击时间
        this.shotInterval = 1;    // 射击间隔

        this.propellerAngle = 0;  // 螺旋桨角度
        this.facing = facing;     // 朝向

        this.simplifiedPhysics = false;  // 是否使用简化物理

        // 初始化碰撞箱
        this.hitBoxes = [
            // 着陆装置
            new Hitbox(-10, 18, 5),  // 左脚
            new Hitbox(10, 18, 5),   // 右脚

            // 顶部碰撞箱
            new Hitbox(20, -15, 20),  // 右上
            new Hitbox(-20, -15, 20), // 左上

            // 机头碰撞箱
            new Hitbox(15, 0, 10),

            // 尾桨碰撞箱
            new Hitbox(-55, -10, 10),
        ];

        // 根据朝向调整碰撞箱位置
        for (const hitbox of this.hitBoxes) {
            hitbox.x *= this.facing;
        }

        // 设置着陆点
        for (const landingHitbox of [
            this.hitBoxes[0],  // 左脚
            this.hitBoxes[1],  // 右脚
        ]) {
            landingHitbox.isLanding = true;
        }

        // 设置关键部位（除着陆装置外都是关键部位）
        for (const hitbox of this.hitBoxes) {
            hitbox.vital = !hitbox.isLanding;
        }

        // 用于存储碰撞箱的全局坐标
        this.globalHitBoxes = [];

        // 物理相关属性
        this.propellerPower = 0;  // 螺旋桨动力
        this.momentum = {x: 0, y: 0, angle: 0};  // 动量（速度）

        // 着陆相关
        this.landed = false;      // 是否已着陆
        this.landedTime = 0;      // 着陆持续时间
        this.lastLanded = 0;      // 上次着陆时间

        // 受损相关
        this.damagedStart = 0;    // 受损开始时间
        this.damagedEnd = 0;      // 受损结束时间

        // 目标锁定系统
        this.lockDuration = 1;                    // 锁定所需时间
        this.lockedTarget = null;                 // 当前锁定的目标
        this.lockedTargetFactor = 0;             // 锁定进度（0-1）
        this.lockedTargetTime = 0;               // 锁定持续时间
        this.lockedTargetAcquireAngle = PI / 8;  // 目标获取角度范围
        this.lockedTargetKeepAngle = PI / 4;     // 目标保持角度范围

        this.lastCollisionSound = 0;  // 上次碰撞音效时间

        this.uncontrollableDuration = 1;  // 失控持续时间
    }

    // 更新碰撞箱的全局坐标
    updateGlobalHitboxes() {
        for (let i = 0 ; i < this.hitBoxes.length ; i++) {
            if (!this.globalHitBoxes[i]) {
                this.globalHitBoxes[i] = new Hitbox(0, 0, 0);
            }

            const hitBox = this.hitBoxes[i];
            // 计算碰撞箱相对于直升机的角度和距离
            const hitBoxAngle = atan2(hitBox.y, hitBox.x);
            const dist = distP(0, 0, hitBox.x, hitBox.y);
            // 根据直升机的位置和角度计算碰撞箱的全局坐标
            this.globalHitBoxes[i].x = this.x + cos(this.angle + hitBoxAngle) * dist;
            this.globalHitBoxes[i].y = this.y + sin(this.angle + hitBoxAngle) * dist;
            this.globalHitBoxes[i].radius = hitBox.radius;
            this.globalHitBoxes[i].isLanding = hitBox.isLanding;
            this.globalHitBoxes[i].vital = hitBox.vital;
            this.globalHitBoxes[i].readjusted = false;
        }
    }

    // 获取所有碰撞箱的平均位置（直升机的中心点）
    get averagePoint() {
        let x = 0, y = 0;
        for (const hitBox of this.globalHitBoxes) {
            x += hitBox.x;
            y += hitBox.y;
        }
        return {
            x: x / this.hitBoxes.length,
            y: y / this.hitBoxes.length,
        };
    }

    // 检查是否可以射击
    get readyToShoot() {
        // 受损期间不能射击
        if (this.age < this.damagedEnd) return false;
        // 检查射击间隔
        return this.age - this.lastShot > this.shotInterval;
    }

    // 获取可以锁定的目标
    // @returns: 最佳目标实体，如果没有合适目标则返回null
    shootingTarget() {
        if (!this.readyToShoot) return null;

        // 计算射击角度（考虑朝向）
        let { angle } = this;
        if (this.facing < 0) {
            angle = atan2(sin(angle), -cos(angle));
        }

        // 寻找最佳目标
        let bestTarget;
        let bestTargetAngleDiff = this.lockedTargetAcquireAngle;
        for (const target of targets(this.world, this)) {
            // 忽略超出射程的目标
            if (dist(target, this) > 600) continue;
            // 不锁定囚犯
            if (target instanceof Prisoner) continue;
            // 非玩家不锁定反叛军
            if (target instanceof Rebel && !this.buckets.includes('player')) continue;

            // 计算与目标的角度差
            const angleToTarget = normalize(angleBetween(this, target));
            const angleDiff = abs(normalize(angleToTarget - normalize(angle)));

            // 如果是当前锁定的目标且在保持角度范围内，直接返回
            if (target === this.lockedTarget && angleDiff < this.lockedTargetKeepAngle) {
                return target;
            }

            // 更新最佳目标
            if (angleDiff < bestTargetAngleDiff) {
                bestTarget = target;
                bestTargetAngleDiff = angleDiff;
            }
        }

        return bestTarget;
    }

    // 更新直升机状态
    // @param elapsed: 经过的时间
    cycle(elapsed) {
        super.cycle(elapsed);

        // 更新螺旋桨角度（根据动力调整转速）
        this.propellerAngle += interpolate(3, 5, this.propellerPower) * PI * elapsed;

        // 处理与其他直升机的碰撞
        for (const chopper of this.world.bucket('chopper')) {
            if (chopper === this) continue;
            if (dist(chopper, this) > this.radius) continue;
            
            // 相互推开
            this.push();
            chopper.push();

            // 播放碰撞音效
            if (this.age - this.lastCollisionSound > 0.2) {
                this.lastCollisionSound = this.age;
                sound(...[,,63,.05,.2,.3,4,,,6,,,.15,1.3,,.1,,.32,.15,.01]); // Explosion 424
            }
        }

        // 目标锁定系统
        const bestTarget = this.shootingTarget();
        if (bestTarget !== this.lockedTarget) {
            // 目标改变，重置锁定进度
            this.lockedTarget = bestTarget;
            this.lockedTargetFactor = 0;
        }

        // 更新锁定进度
        if (this.lockedTarget) {
            this.lockedTargetFactor = min(
                1,
                this.lockedTargetFactor + elapsed / this.lockDuration,
            );
        }

        // 更新锁定时间
        if (this.lockedTargetFactor >= 1) {
            this.lockedTargetTime += elapsed;
        } else {
            this.lockedTargetTime = 0;
        }

        // 处理射击
        if (this.readyToShoot && this.controls.shoot) {
            // 创建导弹
            const missile = new Missile(this);
            this.world.add(missile);

            // 如果有锁定目标，设置导弹方向和目标
            if (this.lockedTarget && this.lockedTargetFactor >= 1) {
                missile.angle = angleBetween(this, this.lockedTarget);
                if (this.buckets.includes('player')) {
                    missile.target = this.lockedTarget;
                }
            }

            this.lastShot = this.age;
        }

        // 更新碰撞箱位置
        this.updateGlobalHitboxes();

        const { averagePoint } = this;

        // 处理与障碍物的碰撞
        for (const obstacle of this.world.bucket('obstacle')) {
            // 优化：只检查附近的障碍物
            if (!isBetween(obstacle.minX - 100, this.x, obstacle.maxX + 100)) continue;

            // 处理每个碰撞箱的碰撞
            for (const hitBox of this.globalHitBoxes) {
                hitBox.readjusted = hitBox.readjusted || obstacle.pushAway(hitBox);
            }
        }

        // 检查着陆和坠毁状态
        const landed = !this.globalHitBoxes.some(hitBox => hitBox.isLanding && !hitBox.readjusted);
        let crashed = this.globalHitBoxes.some(hitBox => (hitBox.vital || this.damagedEnd > this.age) && hitBox.readjusted);

        // 处理与水面的碰撞
        for (const water of this.world.bucket('water')) {
            if (this.y >= water.y) {
                crashed = true;

                // 创建水花效果
                for (let i = 0 ; i < 20 ; i++) {
                    this.world.add(new Fireball(
                        this.x + rnd(-20, 20),
                        this.y + rnd(-20, 20),
                        -rnd(PI / 4, PI * 3 / 4),
                        rnd(400, 600),
                        ['#27c6dc'],
                    ));
                }
            }
        }

        // 如果坠毁，触发爆炸
        if (crashed) {
            this.explode();
            return;
        }

        // 获取碰撞后的新位置
        const newAverage = this.averagePoint;
        const dX = newAverage.x - averagePoint.x;
        const dY = newAverage.y - averagePoint.y;

        if (dX || dY) {
            this.x += dX;
            this.y += dY;
        }

        let idealAngle = 0;
        let angleVelocity = PI / 6;
        if (!landed) {
            if (this.controls.left) idealAngle = -PI / 4;
            if (this.controls.right) idealAngle = PI / 4;
            if (this.controls.left || this.controls.right) angleVelocity = PI / 1.5;
        }

        if (this.age >= this.damagedEnd) {
            let x = 0, y = 0;
            if (this.controls.left) x -= 1;
            if (this.controls.right) x += 1;
            if (this.controls.up) y -= 1;
            if (this.controls.down) y += 1;

            const targetPower = this.controls.up ? 1 : 0;
            this.propellerPower += between(
                -elapsed * 4,
                targetPower - this.propellerPower,
                elapsed * 4,
            );

            this.angle += between(
                -elapsed * angleVelocity,
                idealAngle - this.angle,
                elapsed * angleVelocity
            );
        } else {
            const x = this.x + rnd(-20, 20);
            const y = this.y + rnd(-20, 20);
            const particle = new Particle(
                pick(['#000', '#ff0', '#f80', '#f00']),
                [rnd(10, 20), 0],
                [x, x + rnd(-100, 100)],
                [y, y - rnd(50, 150)],
                rnd(1.2, 3),
            );
            this.world.add(particle);
        }
        this.momentum.angle = 0;

        if (this.simplifiedPhysics) {
            this.momentum.x += cos(idealAngle - PI / 2) * elapsed * 400 * 1.5;
            this.momentum.y -= this.propellerPower * elapsed * 400;
        } else {
            this.momentum.x += interpolate(0.5, 1.5, this.propellerPower) * cos(this.angle - PI / 2) * elapsed * 400;
            this.momentum.y += this.propellerPower * sin(this.angle - PI / 2) * elapsed * 400;
        }

        // Air resistance on X
        const opposition = sign(sin(this.angle)) !== sign(this.momentum.x) || this.simplifiedPhysics
            ? 200
            : 50;
        this.momentum.x += between(
            -elapsed * opposition,
            -this.momentum.x,
            elapsed * opposition,
        );

        // Gravity on Y
        const maxFallSpeed = this.simplifiedPhysics
            ? (this.controls.left || this.controls.right ? 20 : 200)
            : 400;
        const fallAccel = this.simplifiedPhysics
            ? 200
            : (this.controls.down ? 300 : 150);
        this.momentum.y += between(
            -elapsed * (this.simplifiedPhysics ? 150 : 200),
            maxFallSpeed - this.momentum.y,
            elapsed * fallAccel,
        );

        // Cap max momentum on simplified physics
        if (this.simplifiedPhysics) {
            this.momentum.x = between(-300, this.momentum.x, 300);
            this.momentum.y = between(-300, this.momentum.y, 200);
        }

        if (landed && !this.propellerPower) {
            this.momentum.y = 0;
        }

        this.x += this.momentum.x * elapsed;
        this.y += this.momentum.y * elapsed;
        this.angle += this.momentum.angle * elapsed;

        const camera = firstItem(this.world.bucket('camera'));
        // this.x = between(camera.minX, this.x, camera.maxX);
        // this.y = between(camera.minY, this.y, camera.maxY);

        this.angle = between(-PI / 4, this.angle, PI / 4);

        const wasLanded = this.landed;
        const { lastLanded } = this;
        this.landed = landed;
        if (this.landed) {
            this.lastLanded = this.age;
            this.landedTime += elapsed;
        } else {
            this.landedTime = 0;
        }

        if (this.landed && !wasLanded && this.age - lastLanded > 0.5) {
            const [a, b] = this.globalHitBoxes.filter(hitBox => hitBox.isLanding);

            for (let i = 0 ; i < 10 ; i++) {
                const ratio = rnd(-0.5, 1.5);
                const x = a.x + ratio * (b.x - a.x);
                const y = a.y + ratio * (b.y - a.y);
                const particle = new Particle(
                    '#fff',
                    [rnd(10, 15), 0],
                    [x, x + rnd(-30, 30)],
                    [y, y + rnd(-20, -10)],
                    rnd(0.8, 1.5),
                );
                this.world.add(particle);
            }

            sound(...[0.5,,400,.02,.08,.09,4,2.5,-1,9,,,,.3,,.1,.14,.63,.02,,-2063]); // Hit 200
        }

        if (!isBetween(camera.minX, this.x, camera.maxX)) {
            this.momentum.x = 0;
            this.x = between(camera.minX, this.x, camera.maxX);
        }

        if (!isBetween(camera.minY, this.y, camera.maxY)) {
            this.momentum.y = 0;
            this.y = between(camera.minY, this.y, camera.maxY);
        }
    }

    // 推开直升机（用于碰撞和受损处理）
    // @param duration: 失控持续时间（默认1秒）
    push(duration = 1) {
        // 随机设置推力方向和大小
        const angle = rnd(PI / 4, PI * 3 / 4);
        this.momentum.x = cos(angle) * 300;
        this.momentum.y = sin(angle) * 300;

        // 设置受损状态
        this.damagedStart = this.age;
        this.damagedEnd = this.age + this.uncontrollableDuration;
    }

    // 触发直升机爆炸
    explode() {
        this.world.remove(this);
        this.destroy();
        explosion(this.world, this, 80, this);
    }

    // 渲染直升机
    render() {
        ctx.wrap(() => {
            // 设置基本变换（位置、角度、朝向）
            ctx.translate(this.x, this.y);
            ctx.rotate(this.angle);
            ctx.scale(this.facing, 1);

            // 受损时添加旋转效果
            if (this.age < this.damagedEnd) {
                const ratio = (this.age - this.damagedStart) / (this.damagedEnd - this.damagedStart);
                ctx.scale(cos(ratio * PI * 2), 1);
            }

            // 渲染直升机主体
            ctx.fillStyle = '#000';
            // 机身主体
            ctx.fillRect(-20, -15, 40, 30);

            // 尾部
            ctx.fillRect(0, -10, -50, 5);
            ctx.fillRect(-60, -20, 10, 15);

            // 起落架
            ctx.fillRect(-20, 20, 40, 2);
            ctx.fillRect(-10, 15, 2, 5);
            ctx.fillRect(10, 15, 2, 5);

            // 主旋翼轴
            ctx.fillRect(-2, 0, 4, -22);

            // 渲染主旋翼
            ctx.wrap(() => {
                ctx.translate(0, -22);
                ctx.scale(1, 0.45);
                this.renderPropeller(100, 6);
            });

            // 渲染尾桨
            ctx.wrap(() => {
                ctx.translate(-55, -15);
                this.renderPropeller(24, 4);
            });
        });
    }

    // 等待直升机坠毁
    // @returns: Promise，当直升机从世界中移除时解决
    crashed() {
        return this.world.waitFor(() => {
            if (!this.world.contains(this)) throw new Error();
        })
    }

    // 计算直升机1秒后的位置
    // @returns: {x, y} 预测的位置
    futurePosition() {
        const x = this.x + this.momentum.x * 1;
        const y = this.y + this.momentum.y * 1;
        return { x, y };
    }

    // 渲染螺旋桨
    // @param length: 螺旋桨长度
    // @param thickness: 螺旋桨厚度
    renderPropeller(length, thickness) {
        // 根据螺旋桨角度旋转
        ctx.rotate(this.propellerAngle);

        // 渲染螺旋桨主体
        ctx.fillRect(-length / 2, -thickness / 2, length, thickness);

        // 添加旋转模糊效果
        const effectAngle = interpolate(PI / 8, PI / 3, this.propellerPower);

        ctx.globalAlpha = 0.2;
        ctx.beginPath();

        // 绘制上半部分模糊效果
        ctx.moveTo(0, 0);
        ctx.arc(0, 0, length / 2, 0, -effectAngle, true);

        // 绘制下半部分模糊效果
        ctx.rotate(PI);
        ctx.moveTo(0, 0);
        ctx.arc(0, 0, length / 2, 0, -effectAngle, true);

        ctx.fill();
    }
}
